package away3d.bounds ;


import away3d.core.math.Matrix3DUtils ;
import away3d.core.math.Plane3D ;
import away3d.primitives.WireframeCube ;
import away3d.primitives.WireframePrimitiveBase ;
import flash.geom.Matrix3D ;
import flash.geom.Vector3D ;



/**
 * AxisAlignedBoundingBox represents a bounding box volume that has its planes aligned to the local coordinate axes of the bounded object.
 * This is useful for most meshes.
 */
class AxisAlignedBoundingBox extends BoundingVolumeBase
{
    
    
    private var _centerX        : Float ;
    private var _centerY        : Float ;
    private var _centerZ        : Float ;
    private var _halfExtentsX   : Float ;
    private var _halfExtentsY   : Float ;
    private var _halfExtentsZ   : Float ;
    
    
    
    /**
     * Creates a new AxisAlignedBoundingBox object.
     */
    public function new()
    {
        
        _centerX            = 0 ;
        _centerY            = 0 ;
        _centerZ            = 0 ;
        _halfExtentsX       = 0 ;
        _halfExtentsY       = 0 ;
        _halfExtentsZ       = 0 ;
        
    }
    
    
    override private  function updateBoundingRenderable()
    {
        
        _boundingRenderable.scaleX      = Math.max( _halfExtentsX*2, 0.001 ) ;
        _boundingRenderable.scaleY      = Math.max( _halfExtentsY*2, 0.001 ) ;
        _boundingRenderable.scaleZ      = Math.max( _halfExtentsZ*2, 0.001 ) ;
        _boundingRenderable.x           = _centerX ;
        _boundingRenderable.y           = _centerY ;
        _boundingRenderable.z           = _centerZ ;
        
    }
    
    
    override private  function createBoundingRenderable() : WireframePrimitiveBase
    {
        
        return new WireframeCube( 1, 1, 1 ) ;
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function nullify()
    {
        
        super.nullify() ;
        _centerX = _centerY = _centerZ = 0 ;
        _halfExtentsX = _halfExtentsY = _halfExtentsZ = 0 ;
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function isInFrustum( mvpMatrix : Matrix3D ) : Bool
    {
        
        var raw = Matrix3DUtils.RAW_DATA_CONTAINER ;
        
        mvpMatrix.copyRawDataTo( raw ) ;
        
        var c11 = raw[ uint( 0 ) ] ;
        var c12 = raw[ uint( 4 ) ] ;
        var c13 = raw[ uint( 8 ) ] ;
        var c14 = raw[ uint( 12 ) ] ;
        var c21 = raw[ uint( 1 ) ] ;
        var c22 = raw[ uint( 5 ) ] ;
        var c23 = raw[ uint( 9 ) ] ;
        var c24 = raw[ uint( 13 ) ] ;
        var c31 = raw[ uint( 2 ) ] ;
        var c32 = raw[ uint( 6 ) ] ;
        var c33 = raw[ uint( 10 ) ] ;
        var c34 = raw[ uint( 14 ) ] ;
        var c41 = raw[ uint( 3 ) ];
        var c42 = raw[ uint( 7 ) ] ;
        var c43 = raw[ uint( 11 ) ] ;
        var c44 = raw[ uint( 15 ) ] ;
        
        var a   : Float ;
        var b   : Float ;
        var c   : Float ;
        var d   : Float ;
        var dd  : Float ;
        var rr  : Float ;

        // this is basically a p/n vertex test in object space against the frustum planes derived from the mvp
        // with a lot of inlining

    // left plane
        
        a   = c41 + c11 ;
        b   = c42 + c12 ;
        c   = c43 + c13 ;
        d   = c44 + c14 ;
        dd  = a*_centerX + b*_centerY + c*_centerZ ;
        
        if( a < 0 ) a = -a ;
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr  = a*_halfExtentsX + b*_halfExtentsY+ c*_halfExtentsZ ;
        
        if( dd + rr < -d ) return false ;
        
    // right plane
    
        a   = c41 - c11 ;
        b   = c42 - c12 ;
        c   = c43 - c13 ;
        d   = c44 - c14 ;
        dd  = a*_centerX + b*_centerY + c*_centerZ ;
        
        if( a < 0 ) a = -a ;
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr  = a*_halfExtentsX + b*_halfExtentsY+ c*_halfExtentsZ ;
        
        if( dd + rr < -d ) return false ;
        
    // bottom plane
        
        a   = c41 + c21 ;
        b   = c42 + c22 ;
        c   = c43 + c23 ;
        d   = c44 + c24 ;
        dd  = a*_centerX + b*_centerY + c*_centerZ ;
        
        if( a < 0 ) a = -a ;
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr  = a*_halfExtentsX + b*_halfExtentsY+ c*_halfExtentsZ ;
        
        if( dd + rr < -d ) return false ;
        
    // top plane
        
        a   = c41 - c21 ;
        b   = c42 - c22 ;
        c   = c43 - c23 ;
        d   = c44 - c24 ;
        dd = a*_centerX + b*_centerY + c*_centerZ ;
        
        if( a < 0 ) a = -a ; if( b < 0 ) b = -b ; if( c < 0 ) c = -c ;
        
        rr  = a*_halfExtentsX + b*_halfExtentsY+ c*_halfExtentsZ ;
        
        if( dd + rr < -d ) return false ;
        
    // near plane
        
        a   = c31 ;
        b   = c32 ;
        c   = c33 ;
        d   = c34 ;
        dd  = a*_centerX + b*_centerY + c*_centerZ ;
        
        if( a < 0 ) a = -a ;
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr  = a*_halfExtentsX + b*_halfExtentsY+ c*_halfExtentsZ ;
        
        if( dd + rr < -d ) return false ;
        
    // far plane
    
        a   = c41 - c31 ;
        b   = c42 - c32 ;
        c   = c43 - c33 ;
        d   = c44 - c34 ;
        dd  = a*_centerX + b*_centerY + c*_centerZ ;
        
        if( a < 0 ) a = -a ;
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr  = a*_halfExtentsX + b*_halfExtentsY+ c*_halfExtentsZ ;
        
        if( dd + rr < -d ) return false ;
        
        return true ;
        
    }
    
    
    override public function intersectsLine( p: Vector3D<Float>, dir: Vector3D<Float> ) : Bool
    {
        
        var diffX   = p.x - _centerX ;
        var diffY   = p.y - _centerY ;
        var diffZ   = p.z - _centerZ ;
        var dx      = dir.x ;
        var dy      = dir.y ;
        var dz      = dir.z ;
        var crossX  = dy*diffZ - dz*diffY ;
        var crossY  = dz*diffX - dx*diffZ ;
        var crossZ  = dx*diffY - dy*diffX ;

        if( dx < 0 ) dx = -dx ;
        if( dy < 0 ) dy = -dy ;
        if( dz < 0 ) dz = -dz ;
        
        if( crossX < 0 ) crossX = -crossX ;
        if( crossY < 0 ) crossY = -crossY ;
        if( crossZ < 0 ) crossZ = -crossZ ;

        if( crossX > _halfExtentsY * dz + _halfExtentsZ * dy ) return false ;
        if( crossY > _halfExtentsX * dz + _halfExtentsZ * dx ) return false ;
        if( crossZ > _halfExtentsX * dy + _halfExtentsY * dx ) return false ;

        return true ;
        
    }
    
    
    // todo: support intersectsRay
    
    /**
     * @inheritDoc
     */
    override public function fromExtremes( minX : Float, minY : Float, minZ : Float, maxX : Float, maxY : Float, maxZ : Float )
    {
        
        _centerX        = ( maxX + minX )*.5 ;
        _centerY        = ( maxY + minY )*.5 ;
        _centerZ        = ( maxZ + minZ )*.5 ;
        _halfExtentsX   = ( maxX - minX )*.5 ;
        _halfExtentsY   = ( maxY - minY )*.5 ;
        _halfExtentsZ   = ( maxZ - minZ )*.5 ;
        
        super.fromExtremes( minX, minY, minZ, maxX, maxY, maxZ ) ;
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function clone() : BoundingVolumeBase
    {
        
        var clone = new AxisAlignedBoundingBox() ;
        clone.fromExtremes( _min.x, _min.y, _min.z, _max.x, _max.y, _max.z ) ;
        return clone ;
        
    }
    
    
}